import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class client {

	static boolean graphical = false;
    public static void main(String[] args)
    {
        if (true) {
//            pr("You need to supply board number as argument if you want to run online mode");
            pr("OFFLINE MODE");
			client client = new client();
//			String flatBoard = loadBoard("samples", "2");
			String flatBoard = loadBoard("test.txt", "A1");
//			String flatBoard = loadBoard("test.txt", "D2");
//			String flatBoard = loadBoard("test.txt", "C4");
			String res = client.sokoban(flatBoard);
			pr("SOLUTION: "+res);
//			String flatBoard = "###|#";
			
//			U.prB(U.boardToFlatString(b, gs));
//
//			Board b2 = new Board(flatBoard, gs);
//			for(int i = 0; i < lm.size(); i++){
//				U.whatAmI_Move(lm.get(i));
//				gs.doMove(b2, lm.get(i));
//				U.prB(U.boardToFlatString(b2, gs));
//			}
        }else{
			pr("Online mode");
			String address = "130.237.218.85";
			Integer port = 7781;
			Integer map = 1;
			try {
				address=args[0];
				port=Integer.parseInt(args[1]);
				map=Integer.parseInt(args[2]);
				if(args.length>3){
					graphical = args[args.length-1].equals("graphical");
				}else{
					graphical = false;
				}

				Socket lSocket=new Socket(address,port);
				PrintWriter lOut=new PrintWriter(lSocket.getOutputStream());
				BufferedReader lIn=new BufferedReader(new InputStreamReader(lSocket.getInputStream()));

				lOut.println(map);
				lOut.flush();

				String lLine=lIn.readLine();
				int lNumRows=Integer.parseInt(lLine);
				String[] rows = new String[lNumRows];
				for(int i=0;i<lNumRows;i++){
					lLine=lIn.readLine();
					rows[i]=lLine;
				}
				String res = new client().sokoban(parseOnline(rows));
				pr("SOLUTION: "+res);
//				String lMySol="U R R U U L D L L U L L D R R R R L D D R U R U D L L U R";
				//these formats are also valid:
				//String lMySol="URRUULDLLULLDRRRRLDDRURUDLLUR";
				//String lMySol="0 3 3 0 0 2 1 2 2 0 2 2 1 3 3 3 3 2 1 1 3 0 3 0 1 2 2 0 3";

				//send the solution to the server
				lOut.println(res);
				lOut.flush();
				//read answer from the server
				lLine=lIn.readLine();
				pr(lLine);
				args[args.length-1] = lLine;
			}
			catch(Throwable t)
			{
				t.printStackTrace();
			}
		}
        
    }

	String sokoban(String flatBoard){
		pr("board: "+flatBoard);
		U.prB(flatBoard);

		pr("Board after converting and reconverting:");
		GameState gs = new GameState();
		Board b = new Board(flatBoard, gs);
		U.prB(U.boardToFlatString(b, gs, true));

        // Set maxIter.   
        Search.maxIter = 200 + (int)Math.sqrt(Math.pow(b.mCellRows, 2) + Math.pow(b.mCellCols, 2));

		pr("search...\n");
		timer(true, 0);
//		List<Move> lm = Search.search(b, gs);
		List<Move> lm = Search.IDA_search(b, gs);
		timer(false, 0);
		
//		for(int i = 0; i < lm.size(); i++){
//			gs.doMove(b, lm.get(i));
//			U.prB(U.boardToFlatString(b, gs, false));
//		}
        
        pr("ANTAL MOVES: "+lm.size());
        prTime();
        String solution = genSolution(lm);
        pr("nodes: "+b.nodes);
		if(graphical){
            U.prBoardSet(lm, flatBoard, false, 10);
			Graphical g = new Graphical(b, gs, lm, solution);
		}
		return solution;
	}

	String genSolution(List<Move> lm){
		String sol = "";
		for(Move m : lm){
			sol+=getLetterFromMove(m);
		}
		return sol;
	}

	String getLetterFromMove(Move m){
		if(m.isNorth())
			return "U";
		if(m.isSouth())
			return "D";
		if(m.isEast())
			return "R";
		if(m.isWest())
			return "L";

		pr("CAN'T PARSE MOVE TO LETTER");
		System.exit(0);
		return null;
	}

	ArrayList<Date> timer = new ArrayList<Date>();
    ArrayList<Long> totalTime = new ArrayList<Long>();
	void timer(boolean start, int i){
		if(i < timer.size()){
			if(start){
				timer.set(i, new Date());
			}else{
//				U.pr("time: "+timer.get(i).getTime());
				totalTime.set(i, totalTime.get(i) + 1 + (new Date().getTime() - timer.get(i).getTime()));
			}
		}else if(start){
			for(int n = timer.size(); n <= i; n++){
				timer.add(new Date());
				totalTime.add(new Long(0));
			}
		}
	}
	void prTime(){
		for(int i = 0; i < timer.size(); i++){
			U.pr("Time "+i+": "+totalTime.get(i));
		}
	}
	
	

	/* returns a "flat" level. The level:
	 * #####
	 * #@$.#
	 * #####
	 * will be returned as: #####|#@$.#|#####
	 */
	static String loadBoard(String file, String levelName){
		File f = new File(System.getProperty("user.dir")+"/"+file);
		pr("serching for file: "+f.getAbsolutePath());
		String s="";
		if(!f.isFile()){
			pr("file not found! -System exit");
			System.exit(0);
		}else{
			try{
				BufferedReader br = new BufferedReader(new FileReader(f));
				String line;
				while((line = br.readLine()) != null){
					if(line.contains(";LEVEL ")){// beggining of level
						String lvlName = line.split("\\ ")[1];
						if(lvlName.equals(levelName) || levelName.length() == 0){
							while((line = br.readLine()) != null){
								if(!line.contains(";LEVEL ")){
									if(s.length() > 0)
										s+="|";
									s+=line;
								}else{
									return s;
								}
							}
						}
					}
				}
			}catch(FileNotFoundException e){
				pr("file not found! -System exit");
				System.exit(0);
			}catch(IOException e){
				e.printStackTrace();
				System.exit(0);
			}
		}if(s.length() > 0){
			return s;// The level was the last level in the file.
		}
		pr("The level was not found in the file!");
		System.exit(0);
		return "";
	}

	static String parseOnline(String[] s){
		String b = "";
		for(int i = 0; i < s.length; i++){
			if(i>0)
				b+="|";
			b+=s[i];
		}
		return b;
	}

	public static void pr(Object msg){
		System.out.println(""+msg);
	}
}
